package com.learning.mycanvas;

import java.io.File;
import com.facebook.widget.*;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;

import com.learning.assignment_1.MainActivity;
import com.learning.assignment_1.MainActivity.State;
import com.learning.assignment_1.R;
import com.learning.utils.MyConstants;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path.Direction;
import android.graphics.PointF;
import android.text.Layout.Alignment;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.graphics.*;
import android.graphics.drawable.PaintDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.graphics.drawable.shapes.RectShape;
import android.graphics.drawable.shapes.Shape;

public class MyViewUsingCanvas extends View {
	private float maxY = -1;
	private float maxX = -1;
	private Paint myPaint;
	private ArrayList<Matrix> myMatrix;
	private Bitmap myBitmap;
	// private Path myPath;
	private ArrayList<ArrayList<MyObject>> mListObjects;
	private MyObject tempShape;
	private MyObject myBound;
	private PaintDrawable myLine;
	private MainActivity.State currentState;
	private DrawingObjects currentObject;
	private int currentColor = Color.BLACK;
	float startx, starty, odx, ody, mx, my;
	PointF oPoint1, oPoint2;
	PointF startLine, stopLine;
	boolean CW = true;
	private int[] ret;
	private MyImage pickedImage = null;
	private RelativeLayout mainlayout;
	private boolean deleteImage = false;
	public MyViewUsingCanvas(Context context, RelativeLayout parent) {
		super(context);
		mainlayout = parent;
		myPaint = new Paint();
		myMatrix = new ArrayList<Matrix>();
		currentState = MainActivity.State.None;
		currentObject = DrawingObjects.None;
		mListObjects = new ArrayList<ArrayList<MyObject>>();
		// myPath = new Path();
		myPaint.setColor(Color.BLACK);
		myPaint.setStyle(Paint.Style.STROKE);
		myPaint.setStrokeWidth(5f);
		
		// TODO Auto-generated constructor stub
	}

	public enum DrawingObjects {
		Line, Circle, Rectangle, Pencil, Eraser, Text, None
	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		// if (myPath != null)
		// canvas.drawPath(myPath, myPaint);

		// if (startLine != null && stopLine != null)
		// canvas.drawLine(startLine.x, startLine.y, stopLine.x, stopLine.y,
		// myPaint);
		// canvas.save();
		canvas.drawColor(Color.WHITE);
		Integer i = 0;
		
		
		for (Matrix m : myMatrix) {
			canvas.save();
			if (m != null){
				
				canvas.concat(m);
				
			}
//			if (myBitmap != null && i == myMatrix.size()-1) {
//				canvas.drawBitmap(myBitmap, m, new Paint());
//			}

			for (MyObject obj : mListObjects.get(i)) {
//				if(obj instanceof MyImage)
//					((MyImage) obj).setMatrix(m);
				obj.onDraw(canvas);
				
			}
			
			if (tempShape != null && i == myMatrix.size()-1){
				tempShape.onDraw(canvas);
			}
			
			i++;
			canvas.restore();
			// canvas.restore();
			// canvas.setMatrix(new Matrix());
		}
		


	}

	public void drawImage(Bitmap bm) {
		
		MyImage mi= new MyImage(bm,this.getWidth());
		createNewMatrix();
		mListObjects.get(mListObjects.size()-1).add(mi);
		invalidate();
	}

	public void setDrawnObject(DrawingObjects obj) {
		currentObject = obj;
	}

	public void setColorPainter(int color) {
		currentColor = color;
	}

	public void setCurrentState(MainActivity.State state) {
		currentState = state;
	}
	
	public void newEditor(){
		myMatrix.clear();
		mListObjects.clear();
		createNewMatrix();
		invalidate();
	}

	public void createNewMatrix() {
		myMatrix.add(new Matrix());
		mListObjects.add(new ArrayList<MyObject>());
	}
	
	public void zoom(float i,float mx, float my){
		for (Matrix m : myMatrix)
			m.postScale(i, i, mx, my);	
		invalidate();
	}
	
	public int[] findPickedImage() {
		for (int i = mListObjects.size() - 1; i >= 0; i--) {
			for (int j = mListObjects.get(i).size() - 1; j >= 0; j--)
				if (mListObjects.get(i).get(j) instanceof MyImage
						&& ((MyImage) mListObjects.get(i).get(j))
								.checkContains(startx, starty, i))
					return new int[] { i, j };

		}
		return null;
	}
	
	private float[] moveFunction(float x, float y){
		float dx = x - startx;
		float dy = y - starty;
		startx = x;
		starty = y;
		return new float[]{dx, dy};
	}
	
	private float[] zoomFunction(float x0, float x1, float y0, float y1){
		float dx = x0 - x1;
		float dy = y0 - y1;

		float od = (float) Math.sqrt(odx * odx + ody * ody);
		float d = (float) Math.sqrt(dx * dx + dy * dy);

		odx = dx;
		ody = dy;
		return new float[]{d/od, d/od, mx, my};
	}
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction() & MotionEvent.ACTION_MASK) {
		case MotionEvent.ACTION_DOWN:

			startx = event.getX();
			starty = event.getY();
			if (currentObject == DrawingObjects.Pencil) {
				tempShape = new MyPencil();
				((MyPencil) tempShape).mPts.moveTo(startx, starty);
			}
			if (currentObject == DrawingObjects.Line)
				startLine = new PointF(event.getX(), event.getY());

			if (currentObject == DrawingObjects.Eraser) {
				tempShape = new MyEraser();
				((MyEraser) tempShape).mPts.moveTo(startx, starty);
			}
			if (tempShape != null)
				tempShape.setColor(currentColor);
			
			if (currentState == State.ImagePicking){
			    ret= findPickedImage();
				if (ret != null){
						pickedImage = (MyImage) mListObjects.get(ret[0]).get(ret[1]);
						pickedImage.isHighLight(true);
						initializeDeleteButton();
				}
				else
					pickedImage = null;
			}
			break;
		case MotionEvent.ACTION_MOVE:
			if (currentState == State.ImagePicking && pickedImage != null){
				if(event.getPointerCount() == 1) {
					float[] dis = moveFunction(event.getX(), event.getY());
					pickedImage.getMatrix().postTranslate(dis[0], dis[1]);				
					ImageButton dltbtn = (ImageButton) mainlayout.findViewById(MyConstants.DELETEBTNID);
					RectF outSize = new RectF(dltbtn.getLeft(),dltbtn.getTop(),dltbtn.getRight(),dltbtn.getBottom());
					
					if (outSize.contains((int)event.getX()+11,(int)event.getY()+81)){
						dltbtn.setBackgroundResource(R.drawable.delete_image_gray);
						deleteImage = true;
					}
					else{
						dltbtn.setBackgroundResource(R.drawable.delete_image_red);
						deleteImage = false;
					}
				} else {
					float[] ratio = zoomFunction(event.getX(0), event.getX(1), event.getY(0), event.getY(1));
					pickedImage.getMatrix().postScale(ratio[0], ratio[1], ratio[2], ratio[3]);
				}
			}
			if (currentState == State.Transform) {
				if(event.getPointerCount() == 1) {
					float[] dis = moveFunction(event.getX(), event.getY());
					for (Matrix m : myMatrix)
						m.postTranslate(dis[0], dis[1]);
				}
				else if(event.getPointerCount() > 1){
					float[] ratio = zoomFunction(event.getX(0), event.getX(1), event.getY(0), event.getY(1));
					for (Matrix m : myMatrix)
						m.postScale(ratio[0], ratio[1], ratio[2], ratio[3]);
					
				}
			} else if (currentState == State.Rotate
					&& event.getPointerCount() > 1) {
				PointF point2 = oPoint1;
				PointF point1 = oPoint2;
				PointF point3 = new PointF(event.getX(1), event.getY(1));
				for (Matrix m : myMatrix)
					m.postRotate(
							(float) angleBetween2Lines(point1, point2, point3),
							point2.x, point2.y);

				oPoint2 = point3;
			} 
			 else if (currentState == State.Edit) {

				switch (currentObject) {
				case Circle:
					tempShape = new MyShape(new ShapeDrawable());
					((MyShape) tempShape).getObject().getPaint()
							.setStyle(Style.STROKE);
					((MyShape) tempShape).getObject().setBounds(
							(int) (startx > event.getX() ? event.getX()
									: startx),
							(int) (starty > event.getY() ? event.getY()
									: starty),
							(int) (startx < event.getX() ? event.getX()
									: startx),
							(int) (starty < event.getY() ? event.getY()
									: starty));
					((MyShape) tempShape).getObject().setShape(new OvalShape());
					break;
				case Rectangle:
				case Text:
					tempShape = new MyShape(new ShapeDrawable());
					((MyShape) tempShape).getObject().getPaint()
							.setStyle(Style.STROKE);
					((MyShape) tempShape).getObject().setBounds(
							(int) (startx > event.getX() ? event.getX()
									: startx),
							(int) (starty > event.getY() ? event.getY()
									: starty),
							(int) (startx < event.getX() ? event.getX()
									: startx),
							(int) (starty < event.getY() ? event.getY()
									: starty));
					((MyShape) tempShape).getObject().setShape(new RectShape());
					break;
				case Line:
					tempShape = new MyLine(
							new PointF(startLine.x, startLine.y), new PointF(
									event.getX(), event.getY()));
					break;
				case Pencil:
					((MyPencil) tempShape).mPts.lineTo(event.getX(),
							event.getY());
					break;
				case Eraser:
					((MyEraser) tempShape).mPts.lineTo(event.getX(),
							event.getY());
					break;
				}

			}
			if (tempShape != null)
				tempShape.setColor(currentColor);
			break;
		case MotionEvent.ACTION_POINTER_DOWN:
			mx = (event.getX(0) + event.getX(1)) / 2;
			my = (event.getY(0) + event.getY(1)) / 2;
			odx = event.getX(0) - event.getX(1);
			ody = event.getY(0) - event.getY(1);
			oPoint1 = new PointF(event.getX(0), event.getY(0));
			oPoint2 = new PointF(event.getX(1), event.getY(1));
			break;

		case MotionEvent.ACTION_POINTER_UP:
			break;
		case MotionEvent.ACTION_UP:
			if (currentState == State.ImagePicking && pickedImage != null){
				pickedImage.isHighLight(false);
				
				if(deleteImage){
					for (int i = mListObjects.size() - 1; i >= 0; i--) {
						for (int j = mListObjects.get(i).size() - 1; j >= 0; j--)							
							if (mListObjects.get(i).get(j) == pickedImage){
								mListObjects.get(i).remove(j);
								if (mListObjects.get(i).size() == 0){
									mListObjects.remove(i);
									myMatrix.remove(i);
								}
							}

					} 
				}
					
				mainlayout.removeView(mainlayout.findViewById(MyConstants.DELETEBTNID));
			}
			if (tempShape != null) {
				if (currentObject == DrawingObjects.Text){
					final EditText et = new EditText(this.getContext());
					et.setTextSize(14f);
//					et.setImeOptions(EditorInfo.IME_ACTION_DONE);
					et.setSingleLine(true);
					et.setOnEditorActionListener(new OnEditorActionListener() {
						
						@Override
						public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
							// TODO Auto-generated method stub
								if (actionId == EditorInfo.IME_ACTION_DONE) {
									MyText mt = new MyText(((MyShape)tempShape).getObject().getBounds());
									mt.setText(v.getText().toString());
									mListObjects.get(mListObjects.size() - 1).add(mt);
//									
									InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
									imm.hideSoftInputFromWindow(et.getWindowToken(), 0);
									mainlayout.removeView(et);
									
									tempShape = null;
									startLine = null;
									stopLine = null;
									
								 return true;
								 }								
							return false;
						}
					});
					et.setBackgroundColor(Color.TRANSPARENT);
					MyShape wtf = (MyShape) tempShape;
					RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(wtf.getObject().getBounds().width(), wtf.getObject().getBounds().height());
			        rlp.leftMargin = (int)startx;
			        rlp.topMargin = (int)starty;
					mainlayout.addView(et,rlp);

					et.requestFocus();

					InputMethodManager imm = (InputMethodManager)getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
					imm.showSoftInput(et, InputMethodManager.SHOW_IMPLICIT);
				}
				else if (mListObjects.size() > 0 ){
					mListObjects.get(mListObjects.size() - 1).add(tempShape);
					tempShape = null;
					startLine = null;
					stopLine = null;
				}
			}
			break;
		}

		invalidate();
		return true;
	}

	// p2 is the interacting point between two lines
	public void initializeDeleteButton(){
		ImageButton delBtn = new ImageButton(this.getContext());
		delBtn.setId(MyConstants.DELETEBTNID);
		delBtn.setBackgroundResource(R.drawable.delete_image_red);
		RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(100, 100);
        rlp.leftMargin = mainlayout.getWidth()/2 -50;
        rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
		mainlayout.addView(delBtn,rlp);
		
	}
	
	
	public static double angleBetween2Lines(PointF p1, PointF p2, PointF p3) {
		double angle1 = Math.atan2(p1.y - p2.y, p1.x - p2.x);
		double angle2 = Math.atan2(p3.y - p2.y, p3.x - p2.x);
		return (angle2 - angle1) * 180 / Math.PI;
	}

	public PointF getIntersectionPoint(float x1, float y1, float x2, float y2,
			float x3, float y3, float x4, float y4) {

		PointF p = null;

		float d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
		if (d != 0) {
			float xi = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2)
					* (x3 * y4 - y3 * x4))
					/ d;
			float yi = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2)
					* (x3 * y4 - y3 * x4))
					/ d;

			p = new PointF(xi, yi);

		}
		return p;
	}

	private abstract class MyObject {
		protected int mColor = Color.BLACK;
		protected Style mStyle = Style.STROKE;

		public void onDraw(Canvas canvas) {
			myPaint.setColor(mColor);
		}

		public void setColor(int color) {
			mColor = color;
		}

		public void setStyle(Style style) {
			mStyle = style;
		}
	}

	private class MyShape extends MyObject {

		private ShapeDrawable mObj;

		public MyShape(ShapeDrawable obj) {
			mObj = obj;
		}

		@Override
		public void onDraw(Canvas canvas) {
			super.onDraw(canvas);
			mObj.getPaint().setColor(mColor);
			mObj.draw(canvas);
		}

		public ShapeDrawable getObject() {
			return mObj;
		}
	}

	private class MyLine extends MyObject {
		private PointF mStart, mStop;

		public MyLine(PointF start, PointF stop) {
			mStart = start;
			mStop = stop;
		}

		@Override
		public void onDraw(Canvas canvas) {
			super.onDraw(canvas);
			canvas.drawLine(mStart.x, mStart.y, mStop.x, mStop.y, myPaint);
		}
	}

	private class MyPencil extends MyObject {
		private Path mPts;

		public MyPencil() {
			mPts = new Path();
		}

		public Path getPath() {
			return mPts;
		}

		@Override
		public void onDraw(Canvas canvas) {
			super.onDraw(canvas);
			canvas.drawPath(mPts, myPaint);
		}
	}
	
	private class MyImage extends MyObject {
		private Bitmap bm;
		private Matrix mat;
		private RectF mRect;
		private boolean highlight = false;
		public void isHighLight(boolean b){
			highlight = b;
		}
		
		public Matrix getMatrix(){
			return mat;
		}
		public MyImage(Bitmap b, int w) {
			try {
			float ratio = (float)b.getWidth() /(float) b.getHeight();
			
			if (bm!= null)
				bm.recycle();
			bm = Bitmap.createScaledBitmap(b, w,
					(int) (w / ratio), true);
			
			mRect = new RectF(0,0,w,w/ratio);
			mat = new Matrix();
			}
			catch (Exception ex){
				Log.d("loi ngu hoc",ex.getMessage().toString());
			}
		}
		
		@Override
		public void onDraw(Canvas canvas) {
			super.onDraw(canvas);
			
			canvas.drawBitmap(bm, mat, new Paint());
			
			if(highlight){
				ShapeDrawable boundary = new ShapeDrawable();
				boundary.setShape(new RectShape());
				boundary.setBounds(0,0,(int)mRect.right,(int) mRect.bottom);
				boundary.getPaint().setColor(Color.RED);
				boundary.getPaint().setStrokeWidth(2.5f);
				boundary.getPaint().setStyle(Style.STROKE);
				canvas.save();
				canvas.concat(mat);
				boundary.draw(canvas);
				canvas.restore();
			}
		}
		
		public boolean checkContains(float x, float y, int canvasMat){
			RectF cRect = new RectF();
			myMatrix.get(canvasMat).mapRect(cRect, mRect);
			mat.mapRect(cRect);
			return cRect.contains(x, y);
		}
	}
	
	private class MyText extends MyObject {
		private String txt;
		private Rect rct;
		public MyText(Rect rect){
			txt = new String("");
			rct = rect;
		}
		
		private void setText(String invoker){
			txt = invoker;
		}
			
		@Override
		public void onDraw(Canvas canvas){
			Paint mpaint = new Paint();
			mpaint.setTextSize(14f);
			mpaint.setStyle(Style.STROKE);
			mpaint.setStrokeWidth(1);
			canvas.drawText(txt, rct.left, rct.top, mpaint);
		}
	}
	private class MyEraser extends MyObject {
		private Path mPts;

		public MyEraser() {
			mPts = new Path();
		}

		public Path getPath() {
			return mPts;
		}

		@Override
		public void onDraw(Canvas canvas) {
			Paint paint = new Paint();
			paint.setColor(Color.WHITE);
			paint.setStrokeWidth(15f);
			paint.setStyle(Style.STROKE);
			canvas.drawPath(mPts, paint);
		}
	}
}
